/*
 * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package org.eclipse.imagen;

import java.awt.RenderingHints;
import java.awt.image.renderable.ParameterBlock;
import java.util.ListResourceBundle;
import java.util.Locale;
import java.util.ResourceBundle;
import org.eclipse.imagen.media.util.CaselessStringArrayTable;
import org.eclipse.imagen.util.Range;

/**
 * This class provides an abstract implementation of the <code>OperationDescriptor</code> interface that is suitable for
 * subclassing.
 *
 * @see OperationDescriptor
 * @see RegistryElementDescriptor
 */
public abstract class OperationDescriptorImpl implements OperationDescriptor, java.io.Serializable {

    private boolean deprecated = false;

    /**
     * The resource tags and their corresponding data, stored as an two-dimensional <code>String</code> array.
     *
     * @since JAI 1.1
     */
    protected final String[][] resources;

    /**
     * An array of operation modes supported by this operator. Must be a non-empty subset of "rendered", "renderable",
     * "collection" and "renderableCollection" or other image operator modes to be defined later.
     *
     * @since JAI 1.1
     */
    protected final String[] supportedModes;

    /**
     * A <code>CaselessStringArrayTable</code> mapping the mode names to their indices in the above arrays in a
     * case-insensitive manner.
     */
    private CaselessStringArrayTable modeIndices;

    /**
     * An array of <code>String</code>s that are the names of the sources of this operation. The names must be listed in
     * the order corresponding to the source <code>Class</code>es.
     *
     * @since JAI 1.1
     */
    protected final String[] sourceNames;

    /**
     * A 2D array of source classes for each source for each mode. sourceClasses[m][i] specifies the <code>Class</code>
     * for supportedModes[m] and sourceNames[i].
     */
    private Class[][] sourceClasses;

    /**
     * A <code>CaselessStringArrayTable</code> mapping the source names to their indices in the above arrays in a
     * case-insensitive manner.
     */
    private CaselessStringArrayTable sourceIndices;

    /** An array of <code>ParameterListDescriptor</code> for each mode. */
    private ParameterListDescriptor[] paramListDescriptors;

    /**
     * The array of parameter names. We need this because ParameterListDescriptor works only with parameter names and
     * not parameter indices. But many of the deprecated methods and the validation of ParameterBlock has to happen
     * through parameter indices.
     */
    String[] paramNames;

    /** The global name of this operation. */
    private String name = null;

    // Constructors...

    /** Do some of the initialization common to all constructors. */
    private String[] checkSources(
            String[][] resources, String[] supportedModes, String[] sourceNames, Class[][] sourceClasses) {

        if ((resources == null) || (resources.length == 0))
            throw new IllegalArgumentException("resources: " + JaiI18N.getString("Generic2"));

        if ((supportedModes == null) || (supportedModes.length == 0))
            throw new IllegalArgumentException("supportedModes: " + JaiI18N.getString("Generic2"));

        // Validate source related arguments.

        int numModes = supportedModes.length;

        if (sourceClasses != null) {

            if (sourceClasses.length != numModes)
                throw new IllegalArgumentException(JaiI18N.formatMsg(
                        "OperationDescriptorImpl0", new Object[] {"sourceClasses", new Integer(numModes)}));

            int numSources = (sourceClasses[0] == null) ? 0 : sourceClasses[0].length;

            if (sourceNames == null) {
                sourceNames = getDefaultSourceNames(numSources);

            } else if (sourceNames.length != numSources) {

                throw new IllegalArgumentException(JaiI18N.formatMsg(
                        "OperationDescriptorImpl1",
                        new Object[] {new Integer(sourceNames.length), new Integer(numSources)}));
            }

            for (int i = 0; i < sourceClasses.length; i++) {
                int ns = (sourceClasses[i] == null) ? 0 : sourceClasses[i].length;

                if (numSources != ns) {
                    throw new IllegalArgumentException(JaiI18N.formatMsg(
                            "OperationDescriptorImpl2",
                            new Object[] {new Integer(ns), new Integer(numSources), supportedModes[i]}));
                }
            }

        } else if ((sourceNames != null) && (sourceNames.length != 0)) {
            throw new IllegalArgumentException(JaiI18N.formatMsg(
                    "OperationDescriptorImpl1", new Object[] {new Integer(sourceNames.length), new Integer(0)}));
        }

        return sourceNames;
    }

    /**
     * Constructor. Note that <code>sourceClasses[m][i]</code> corresponds to the mode <code>supportedModes[m]</code>
     * and the source <code>sourceNames[i]</code>. Similarly <code>paramClasses[m][i]</code> corresponds to the mode
     * <code>supportedModes[m]</code> and the parameter <code> paramNames[i]</code>. The same holds true for <code>
     * paramDefaults</code> and <code>validParamValues</code>
     *
     * @param resources The resource tags and their corresponding data.
     * @param supportedModes The modes that this operator supports. maybe one or more of "rendered", "renderable",
     *     "collection", and "renderableCollection" (or other image operation related modes that maybe defined later).
     *     Must support at least one mode.
     * @param sourceNames The source names. It may be <code>null</code> if this operation has no sources or if the
     *     default source naming convention ("source0", "source1", etc.) is to be used.
     * @param sourceClasses The source types required by this operation for each of the above supported modes. can be
     *     null if this operation has no sources. The number of sources for each mode must be the same.
     * @param paramNames The localized parameter names. It may be <code>null</code> if this operation has no parameters.
     * @param paramClasses The parameter types required by this operation. for each mode. It may be <code>null</code> if
     *     this operation has no parameters. The number of parameters for each mode must be the same.
     * @param paramDefaults The parameter default values for each parameter for each mode. It may be <code>null</code>
     *     if this operation has no parameters, or none of the parameters has a default value for any mode. The
     *     parameter defaults for an individual mode may be null, if there are no defaults for that mode.
     * @param validParamValues defines the valid values for each parameter for each mode. this can be <code>null</code>
     *     if the operation has no parameters. Otherwise each element can be filled in as defined in
     *     {@link ParameterListDescriptorImpl#ParameterListDescriptorImpl( Object, String[], Class[], Object[],
     *     Object[])}
     * @throws IllegalArgumentException if <code>resources</code> is <code>null</code>.
     * @throws IllegalArgumentException if supportedModes is <code>null</code>
     * @throws IllegalArgumentException if the number of <code>sourceClasses</code> for each mode is not the same or is
     *     not equal to the number of sourceNames (if non-null).
     * @throws IllegalArgumentException if this operation has parameters and <code>paramClasses</code> or <code>
     *     paramNames</code> is <code>null</code>.
     * @throws IllegalArgumentException if <code>sourceNames</code> is non-<code>null</code> and its length does not
     *     equal the number of sources of this operation.
     * @throws IllegalArgumentException if this operation has parameters and <code>paramClasses</code>, <code>paramNames
     *     </code>, and <code>paramDefaults</code> (if all are not <code>null</code>) do not all have the same number of
     *     elements.
     * @since JAI 1.1
     */
    public OperationDescriptorImpl(
            String[][] resources,
            String[] supportedModes,
            String[] sourceNames,
            Class[][] sourceClasses,
            String[] paramNames,
            Class[][] paramClasses,
            Object[][] paramDefaults,
            Object[][] validParamValues) {

        sourceNames = checkSources(resources, supportedModes, sourceNames, sourceClasses);

        this.resources = resources;
        this.supportedModes = supportedModes;
        this.sourceNames = sourceNames;
        this.sourceClasses = sourceClasses;
        this.paramNames = paramNames;

        this.modeIndices = new CaselessStringArrayTable(supportedModes);
        this.sourceIndices = new CaselessStringArrayTable(sourceNames);

        // Validate parameter related arguments.

        int numParams = (paramNames == null) ? 0 : paramNames.length;
        int numModes = supportedModes.length;

        if (numParams == 0) {
            if ((paramClasses != null) && (paramClasses.length != numModes))
                throw new IllegalArgumentException(JaiI18N.formatMsg(
                        "OperationDescriptorImpl0", new Object[] {"paramClasses", new Integer(numModes)}));

        } else {

            if ((paramClasses == null) || (paramClasses.length != numModes))
                throw new IllegalArgumentException(JaiI18N.formatMsg(
                        "OperationDescriptorImpl0", new Object[] {"paramClasses", new Integer(numModes)}));
        }

        if ((paramDefaults != null) && (paramDefaults.length != numModes))
            throw new IllegalArgumentException(JaiI18N.formatMsg(
                    "OperationDescriptorImpl0", new Object[] {"paramDefaults", new Integer(numModes)}));

        if ((validParamValues != null) && (validParamValues.length != numModes))
            throw new IllegalArgumentException(JaiI18N.formatMsg(
                    "OperationDescriptorImpl0", new Object[] {"validParamValues", new Integer(numModes)}));

        // Create the ParameterListDescriptor-s for each mode.

        paramListDescriptors = new ParameterListDescriptor[numModes];

        for (int i = 0; i < numModes; i++) {
            paramListDescriptors[i] = new ParameterListDescriptorImpl(
                    this,
                    paramNames,
                    paramClasses[i],
                    paramDefaults == null ? null : paramDefaults[i],
                    validParamValues == null ? null : validParamValues[i]);
        }
    }

    /**
     * Constructor. This assumes that all modes have the same set of parameter classes, defaults and valid values. Note
     * that <code>sourceClasses[m][i]</code> corresponds to the mode <code>supportedModes[m]</code> and the source
     * <code>sourceNames[i]</code>.
     *
     * @param resources The resource tags and their corresponding data.
     * @param supportedModes The modes that this operator supports. maybe one or more of "rendered", "renderable",
     *     "collection", and "renderableCollection". Must support at least one mode.
     * @param sourceNames The source names. It may be <code>null</code> if this operation has no sources or if the
     *     default source naming convention ("source0", "source1", etc.) is to be used.
     * @param sourceClasses The source types required by this operation for each of the above supported modes. can be
     *     null if this operation has no sources. The number of sources for each mode must be the same.
     * @param paramNames The localized parameter names. It may be <code>null</code> if this operation has no parameters.
     * @param paramClasses The parameter types required by this operation. It may be <code>null</code> if this operation
     *     has no parameters.
     * @param paramDefaults The parameter default values for each parameter It may be <code>null</code> if this
     *     operation has no parameters, or none of the parameters has a default value.
     * @param validParamValues defines the valid values for each parameter for all modes. this can be <code>null</code>
     *     if the operation has no parameters. Otherwise it can be filled in as defined in
     *     {@link ParameterListDescriptorImpl#ParameterListDescriptorImpl( Object, String[], Class[], Object[],
     *     Object[])}
     * @throws IllegalArgumentException if <code>resources</code> is <code>null</code>.
     * @throws IllegalArgumentException if supportedModes is <code>null</code>
     * @throws IllegalArgumentException if the number of <code>sourceClasses</code> for each mode is not the same or is
     *     not equal to the number of sourceNames (if non-null).
     * @throws IllegalArgumentException if this operation has parameters and <code>paramClasses</code> or <code>
     *     paramNames</code> is <code>null</code>.
     * @throws IllegalArgumentException if <code>sourceNames</code> is non-<code>null</code> and its length does not
     *     equal the number of sources of this operation.
     * @throws IllegalArgumentException if this operation has parameters and <code>paramClasses</code>, <code>paramNames
     *     </code>, and <code>paramDefaults</code> (if all are not <code>null</code>) do not all have the same number of
     *     elements.
     * @since JAI 1.1
     */
    public OperationDescriptorImpl(
            String[][] resources,
            String[] supportedModes,
            String[] sourceNames,
            Class[][] sourceClasses,
            String[] paramNames,
            Class[] paramClasses,
            Object[] paramDefaults,
            Object[] validParamValues) {

        sourceNames = checkSources(resources, supportedModes, sourceNames, sourceClasses);

        this.resources = resources;
        this.supportedModes = supportedModes;
        this.sourceNames = sourceNames;
        this.sourceClasses = sourceClasses;
        this.paramNames = paramNames;

        this.modeIndices = new CaselessStringArrayTable(supportedModes);
        this.sourceIndices = new CaselessStringArrayTable(sourceNames);

        // Create one ParameterListDescriptor and use the same for each mode.

        ParameterListDescriptor pld =
                new ParameterListDescriptorImpl(this, paramNames, paramClasses, paramDefaults, validParamValues);

        paramListDescriptors = new ParameterListDescriptor[supportedModes.length];

        for (int i = 0; i < supportedModes.length; i++) {
            paramListDescriptors[i] = pld;
        }
    }

    /**
     * Constructor. This assumes that all modes have the same set of parameter classes, defaults and valid values. The
     * source names are automatically generated using the default source naming convertion ("source0", "source1", etc.).
     * The source class list is automatically generated using <code>makeDefaultSourceClassList()</code> from <code>
     * numSources</code> and <code>supportedModes</code>
     *
     * @param resources The resource tags and their corresponding data.
     * @param supportedModes The modes that this operator supports. maybe one or more of "rendered", "renderable",
     *     "collection", and "renderableCollection". Must support at least one mode.
     * @param numSources The number of sources.
     * @param paramNames The localized parameter names. It may be <code>null</code> if this operation has no parameters.
     * @param paramClasses The parameter types required by this operation. It may be <code>null</code> if this operation
     *     has no parameters.
     * @param paramDefaults The parameter default values for each parameter It may be <code>null</code> if this
     *     operation has no parameters, or none of the parameters has a default value.
     * @param validParamValues defines the valid values for each parameter for all modes. this can be <code>null</code>
     *     if the operation has no parameters. Otherwise it can be filled in as defined in
     *     {@link ParameterListDescriptorImpl#ParameterListDescriptorImpl( Object, String[], Class[], Object[],
     *     Object[])}
     * @throws IllegalArgumentException if <code>resources</code> is <code>null</code>.
     * @throws IllegalArgumentException if supportedModes is <code>null</code>
     * @throws IllegalArgumentException if this operation has parameters and <code>paramClasses</code> or <code>
     *     paramNames</code> is <code>null</code>.
     * @throws IllegalArgumentException if this operation has parameters and <code>paramClasses</code>, <code>paramNames
     *     </code>, and <code>paramDefaults</code> (if all are not <code>null</code>) do not all have the same number of
     *     elements.
     * @since JAI 1.1
     */
    public OperationDescriptorImpl(
            String[][] resources,
            String[] supportedModes,
            int numSources,
            String[] paramNames,
            Class[] paramClasses,
            Object[] paramDefaults,
            Object[] validParamValues) {

        Class[][] sourceClasses = makeDefaultSourceClassList(supportedModes, numSources);

        String[] sourceNames = checkSources(resources, supportedModes, null, sourceClasses);

        this.resources = resources;
        this.supportedModes = supportedModes;
        this.sourceNames = sourceNames;
        this.sourceClasses = sourceClasses;
        this.paramNames = paramNames;

        this.modeIndices = new CaselessStringArrayTable(supportedModes);
        this.sourceIndices = new CaselessStringArrayTable(sourceNames);

        // Create one ParameterListDescriptor and use the same for each mode.

        ParameterListDescriptor pld =
                new ParameterListDescriptorImpl(this, paramNames, paramClasses, paramDefaults, validParamValues);

        paramListDescriptors = new ParameterListDescriptor[supportedModes.length];

        for (int i = 0; i < supportedModes.length; i++) {
            paramListDescriptors[i] = pld;
        }
    }

    /**
     * Constructor which accepts a <code>ParameterListDescriptor</code> to describe the parameters for each mode. Note
     * that <code>sourceClasses[m][i]</code> corresponds to the mode <code>supportedModes[m]</code> and the source
     * <code>sourceNames[i]</code>.
     *
     * @param resources The resource tags and their corresponding data.
     * @param supportedModes The modes that this operator supports. maybe one or more of "rendered", "renderable",
     *     "collection", and "renderableCollection". Must support at least one mode.
     * @param sourceNames The source names. It may be <code>null</code> if this operation has no sources or if the
     *     default source naming convention ("source0", "source1", etc.) is to be used.
     * @param sourceClasses The source types required by this operation for each of the above supported modes. can be
     *     null if this operation has no sources. The number of sources for each mode must be the same.
     * @param pld the parameter list descriptor for each mode. Can be <code>null</code> if there are no parameters.
     * @throws IllegalArgumentException if <code>resources</code> is <code>null</code>.
     * @throws IllegalArgumentException if supportedModes is <code>null</code>
     * @throws IllegalArgumentException if the number of <code>sourceClasses</code> for each mode is not the same or is
     *     not equal to the number of sourceNames (if non-null).
     * @throws IllegalArgumentException if <code>sourceNames</code> is non-<code>null</code> and its length does not
     *     equal the number of sources of this operation.
     * @since JAI 1.1
     */
    public OperationDescriptorImpl(
            String[][] resources,
            String[] supportedModes,
            String[] sourceNames,
            Class[][] sourceClasses,
            ParameterListDescriptor[] pld) {

        sourceNames = checkSources(resources, supportedModes, sourceNames, sourceClasses);

        this.resources = resources;
        this.supportedModes = supportedModes;
        this.sourceNames = sourceNames;
        this.sourceClasses = sourceClasses;

        this.modeIndices = new CaselessStringArrayTable(supportedModes);
        this.sourceIndices = new CaselessStringArrayTable(sourceNames);

        if ((pld != null) && (pld.length != supportedModes.length)) {
            throw new IllegalArgumentException(JaiI18N.formatMsg(
                    "OperationDescriptorImpl0",
                    new Object[] {"ParameterListDescriptor's", new Integer(supportedModes.length)}));
        }

        if (pld == null) {

            ParameterListDescriptor tpld = new ParameterListDescriptorImpl();

            paramListDescriptors = new ParameterListDescriptor[supportedModes.length];

            for (int i = 0; i < supportedModes.length; i++) paramListDescriptors[i] = tpld;

            this.paramNames = null;
        } else {
            paramListDescriptors = pld;
            this.paramNames = paramListDescriptors[0].getParamNames();
        }
    }

    /**
     * Constructor. This assumes that all modes use the same <code>ParameterListDescriptor</code>. Note that <code>
     * sourceClasses[m][i]</code> corresponds to the mode <code>supportedModes[m]</code> and the source <code>
     * sourceNames[i]</code>.
     *
     * @param resources The resource tags and their corresponding data.
     * @param supportedModes The modes that this operator supports. maybe one or more of "rendered", "renderable",
     *     "collection", and "renderableCollection". Must support at least one mode.
     * @param sourceNames The source names. It may be <code>null</code> if this operation has no sources or if the
     *     default source naming convention ("source0", "source1", etc.) is to be used.
     * @param sourceClasses The source types required by this operation for each of the above supported modes. can be
     *     null if this operation has no sources. The number of sources for each mode must be the same.
     * @param pld the parameter list descriptor for all modes. Can be <code>null</code> if there are no parameters.
     * @throws IllegalArgumentException if <code>resources</code> is <code>null</code>.
     * @throws IllegalArgumentException if supportedModes is <code>null</code>
     * @throws IllegalArgumentException if the number of <code>sourceClasses</code> for each mode is not the same or is
     *     not equal to the number of sourceNames (if non-null).
     * @throws IllegalArgumentException if <code>sourceNames</code> is non-<code>null</code> and its length does not
     *     equal the number of sources of this operation.
     * @since JAI 1.1
     */
    public OperationDescriptorImpl(
            String[][] resources,
            String[] supportedModes,
            String[] sourceNames,
            Class[][] sourceClasses,
            ParameterListDescriptor pld) {

        sourceNames = checkSources(resources, supportedModes, sourceNames, sourceClasses);

        this.resources = resources;
        this.supportedModes = supportedModes;
        this.sourceNames = sourceNames;
        this.sourceClasses = sourceClasses;

        this.modeIndices = new CaselessStringArrayTable(supportedModes);
        this.sourceIndices = new CaselessStringArrayTable(sourceNames);

        if (pld == null) pld = new ParameterListDescriptorImpl();

        this.paramNames = pld.getParamNames();

        paramListDescriptors = new ParameterListDescriptor[supportedModes.length];

        for (int i = 0; i < supportedModes.length; i++) {
            paramListDescriptors[i] = pld;
        }
    }

    /** Gets the default source names. */
    private String[] getDefaultSourceNames(int numSources) {

        String[] defaultSourceNames = new String[numSources];
        for (int i = 0; i < numSources; i++) {
            defaultSourceNames[i] = "source" + i;
        }
        return defaultSourceNames;
    }

    // BEGIN : RegistryElementDescriptor methods

    /**
     * Returns the name of this operation; this is the same as the <code>GlobalName</code> value in the resources and is
     * visible to all. This is also descriptor name under which it is registered in the <code>OperationRegistry</code>.
     *
     * @return A <code>String</code> representing the operation's global name.
     * @throws MissingResourceException if the <code>GlobalName</code> resource value is not supplied in the <code>
     *     resources</code>.
     */
    public String getName() {
        if (name == null) {
            name = (String) getResourceBundle(Locale.getDefault()).getObject("GlobalName");
        }
        return name;
    }

    /**
     * The registry modes supported by this descriptor. Known modes include those returned by <code>
     * RegistryMode.getModes()</code>
     *
     * @return an array of <code>String</code>s specifying the supported modes.
     * @see RegistryMode
     * @see RegistryElementDescriptor
     * @since JAI 1.1
     */
    public String[] getSupportedModes() {
        return supportedModes;
    }

    /**
     * Does this descriptor support the specified registry mode ?. The <code>modeName</code>s are treated in a
     * case-insensitive (but retentive) manner.
     *
     * @param modeName the registry mode name
     * @return true, if the implementation of this descriptor supports the specified mode. false otherwise.
     * @throws IllegalArgumentException if <code>modeName</code> is null
     * @see RegistryElementDescriptor
     * @since JAI 1.1
     */
    public boolean isModeSupported(String modeName) {

        if (modeName == null) throw new IllegalArgumentException(JaiI18N.getString("Generic0"));

        return modeIndices.contains(modeName);
    }

    /**
     * Does this descriptor support properties ?
     *
     * @return The default implementation in this class returns <code>true</code> so that properties are always
     *     supported. Operations that do not wish to support properties must override this implementation.
     * @see PropertyGenerator
     * @see RegistryElementDescriptor
     * @since JAI 1.1
     */
    public boolean arePropertiesSupported() {
        return true;
    }

    /**
     * Returns an array of <code>PropertyGenerator</code>s implementing the property inheritance for this operator that
     * may be used as a basis for the operation's property management. The default implementation returns <code>null
     * </code>, indicating that source properties are simply copied. Subclasses should override this method if they wish
     * to produce inherited properties.
     *
     * <p><strong> For the sake of backward compatibilty, if a deprecated constructor was used to create this object,
     * then this method simply calls its deprecated equivalent, <code>getPropertyGenerators()</code>, if the modeName is
     * either "rendered" or "renderable". </strong>
     *
     * @param modeName the registry mode name
     * @return An array of <code>PropertyGenerator</code>s, or <code>null</code> if this operation does not have any of
     *     its own <code>PropertyGenerator</code>s.
     * @throws IllegalArgumentException if <code>modeName</code> is null or if it is not one of the supported modes.
     * @throws UnsupportedOperationException if <code>arePropertiesSupported</code> returns <code>false</code>
     * @see RegistryElementDescriptor
     * @since JAI 1.1
     */
    public PropertyGenerator[] getPropertyGenerators(String modeName) {

        if (modeName == null) throw new IllegalArgumentException(JaiI18N.getString("Generic0"));

        if (deprecated) {
            if (modeName.equalsIgnoreCase("rendered") || modeName.equalsIgnoreCase("renderable"))
                return getPropertyGenerators();
        }

        if (!arePropertiesSupported()) {
            throw new UnsupportedOperationException(
                    JaiI18N.formatMsg("OperationDescriptorImpl3", new Object[] {modeName}));
        }

        return null;
    }

    /**
     * Returns the <code>ParameterListDescriptor</code> that describes the associated parameters (NOT sources). This
     * method returns null if there are no parameters for the specified modeName. If the specified modeName supports
     * parameters but the implementing class does not have parameters, then this method returns a non-null <code>
     * ParameterListDescriptor</code> whose <code>getNumParameters()</code> returns 0.
     *
     * @param modeName the registry mode name.
     * @throws IllegalArgumentException if <code>modeName</code> is null or if it is not one of the supported modes.
     * @see RegistryElementDescriptor
     * @since JAI 1.1
     */
    public ParameterListDescriptor getParameterListDescriptor(String modeName) {
        return paramListDescriptors[modeIndices.indexOf(modeName)];
    }

    // END : RegistryElementDescriptor methods

    /**
     * Returns the resource data for this operation. It must contain <code>String</code> data for the following tags:
     * "GlobalName", "LocalName", "Vendor", "Description", "DocURL", and "Version". Additional resources should be
     * supplied when appropriate.
     *
     * <p>The default implementation simply returns a reference to the local "resources" variable, which should be
     * supplied by each subclass by way of the superclass constructor. It also ignores the <code>Locale</code> argument,
     * and always returns the <code>String</code>s in the default <code>Locale</code>.
     *
     * @param locale The <code>Locale</code> in which to localize the resource data.
     * @return A two-dimensional array of <code>String</code>s containing the mandatory and optional resource tags and
     *     their corresponding resource data. (String[i][0] is the tag for the i-th resource and String[i][1] is the
     *     corresponding data)
     */
    public String[][] getResources(Locale locale) {
        return resources;
    }

    /**
     * Returns the resource data for this operation in a <code>ResourceBundle</code>. The resource data are taken from
     * the <code>getResources()</code> method.
     *
     * <p>The default implementation ignores the <code>Locale</code> argument, and always returns the resources in the
     * default <code>Locale</code>.
     *
     * @param locale The <code>Locale</code> in which to localize the resource data.
     * @return A <code>ResourceBundle</code> containing mandatory and optional resource information.
     */
    public ResourceBundle getResourceBundle(Locale locale) {
        final Locale l = locale;
        return new ListResourceBundle() {
            public Object[][] getContents() {
                return getResources(l);
            }
        }; // from return statement
    }

    /** Returns the number of sources required by this operation. All modes have the same number of sources. */
    public int getNumSources() {
        return sourceNames.length;
    }

    /**
     * Returns an array of <code>Class</code>es that describe the types of sources required by this operation for the
     * specified mode. If this operation has no sources, this method returns <code>null</code>.
     *
     * @param modeName the operation mode name
     * @throws IllegalArgumentException if modeName is <code>null</code> or if it is not one of the supported modes.
     * @since JAI 1.1
     */
    public Class[] getSourceClasses(String modeName) {
        checkModeName(modeName);

        Class[] sc = sourceClasses[modeIndices.indexOf(modeName)];

        if ((sc != null) && (sc.length <= 0)) return null;

        return sc;
    }

    /**
     * Returns an array of <code>String</code>s that are the names of the sources of this operation. If this operation
     * has no sources, this method returns <code>null</code>. If this operation has sources but their names were not
     * provided at construction time, then the returned names will be of the form "source0", "source1", etc.
     *
     * @since JAI 1.1
     */
    public String[] getSourceNames() {

        if ((sourceNames == null) || (sourceNames.length <= 0)) return null;

        return sourceNames;
    }

    /**
     * Returns a <code>Class</code> that describes the type of destination this operation produces for the specified
     * mode.
     *
     * <p><strong> For the sake of backward compatibilty, if a deprecated constructor was used to create this object,
     * then this method simply calls its deprecated equivalent, if the modeName is either "rendered" or "renderable".
     * </strong>
     *
     * @param modeName the operation mode name
     * @throws IllegalArgumentException if modeName is <code>null</code> or if it is not one of the supported modes.
     * @since JAI 1.1
     */
    public Class getDestClass(String modeName) {

        checkModeName(modeName);

        if (deprecated) {

            if (modeName.equalsIgnoreCase("rendered")) return getDestClass();

            if (modeName.equalsIgnoreCase("renderable")) return getRenderableDestClass();
        }

        return RegistryMode.getMode(modeName).getProductClass();
    }

    /**
     * Returns <code>true</code> if this operation supports the specified mode, and is capable of handling the given
     * input source(s) for the specified mode. The default implementation ensures that the <code>ParameterBlock</code>
     * has at least the required number of sources. It also verifies the class type of the first <code>getNumSources()
     * </code> and makes sure that none of them are <code>null</code>. Any extra sources in the <code>ParameterBlock
     * </code> are ignored. Subclasses should override this implementation if their requirement on the sources are
     * different from the default. This method is used by <code>validateArguments</code> to validate the sources.
     *
     * <p><strong> For the sake of backward compatibilty, if a deprecated constructor was used to create this object,
     * then this method simply calls its deprecated equivalent, if the <code>modeName</code> is either "rendered" or
     * "renderable". </strong>
     *
     * @param modeName the operation mode name
     * @param args a <code>ParameterBlock</code> that has the sources
     * @param msg A string that may contain error messages.
     * @throws IllegalArgumentException if any of the input parameters are <code>null</code>.
     * @since JAI 1.1
     * @see #validateArguments
     */
    protected boolean validateSources(String modeName, ParameterBlock args, StringBuffer msg) {
        if (modeName == null) throw new IllegalArgumentException(JaiI18N.getString("Generic0"));

        if (deprecated) {

            if (modeName.equalsIgnoreCase("rendered")) return validateSources(args, msg);

            if (modeName.equalsIgnoreCase("renderable")) return validateRenderableSources(args, msg);
        }

        return validateSources(getSourceClasses(modeName), args, msg);
    }

    /**
     * Returns <code>true</code> if this operation is capable of handling the input parameters for the specified mode.
     * The default implementation validates the number of parameters, the class type of each parameter, and null
     * parameter. For non-null parameters, it also checks to see if the parameter value is valid. Subclasses should
     * override this implementation if their requirement on the parameter objects are different from the default. This
     * is used by <code>validateArguments</code> to validate the parameters.
     *
     * <p>JAI allows unspecified tailing parameters if these parameters have default values. This method automatically
     * sets these unspecified parameters to their default values. However, if a parameter, which has a default value, is
     * followed by one or more parameters that have no default values, this parameter must be specified in the <code>
     * ParameterBlock</code>; else this method returns <code>false</code>.
     *
     * <p>This method sets all the undefined parameters in the <code>ParameterBlock</code> to their default values, if
     * the default values are specified.
     *
     * <p>Note that <code>DeferredData</code> parameters will not be recognized as valid unless the parameter is defined
     * to have class <code>DeferredData.class</code>.
     *
     * <p><strong> For the sake of backward compatibilty, if a deprecated constructor was used to create this object,
     * then this method simply calls its deprecated equivalent, if the <code>modeName</code> is either "rendered" or
     * "renderable". </strong>
     *
     * @throws IllegalArgumentException if any of the input parameters are <code>null</code>.
     * @since JAI 1.1
     * @see #validateArguments
     * @see ParameterListDescriptorImpl#isParameterValueValid
     */
    protected boolean validateParameters(String modeName, ParameterBlock args, StringBuffer msg) {
        if (modeName == null) throw new IllegalArgumentException(JaiI18N.getString("Generic0"));

        if (deprecated) {
            if (modeName.equalsIgnoreCase("rendered") || modeName.equalsIgnoreCase("renderable"))
                return validateParameters(args, msg);
        }

        return validateParameters(getParameterListDescriptor(modeName), args, msg);
    }

    /**
     * Returns <code>true</code> if this operation/mode is capable of handling the input source(s) and/or parameter(s)
     * specified in the <code>ParameterBlock</code>, or <code>false</code> otherwise, in which case an explanatory
     * message may be appended to the <code>StringBuffer</code>.
     *
     * <p>This method is the standard place where input arguments are validated against this operation's specification
     * for the specified mode. It is called by <code>JAI.create()</code> as a part of its validation process. Thus it is
     * strongly recommended that the application programs use the <code>JAI.create()</code> methods to instantiate all
     * the rendered operations.
     *
     * <p>The default implementation of this method makes sure that this operator supports the specified mode and then
     * calls <code>validateSources</code> and <code>validateParameters</code>.
     *
     * <p>This method sets all the undefined parameters in the <code>ParameterBlock</code> to their default values, if
     * the default values are specified.
     *
     * <p>Note that <code>DeferredData</code> parameters will not be recognized as valid unless the parameter is defined
     * to have class <code>DeferredData.class</code>.
     *
     * @param modeName the operation mode name
     * @param args Input arguments, including source(s) and/or parameter(s).
     * @param msg A string that may contain error messages.
     * @throws IllegalArgumentException if <code>modeName</code> is <code>null</code>
     * @since JAI 1.1
     * @see #validateSources
     * @see #validateParameters
     */
    public boolean validateArguments(String modeName, ParameterBlock args, StringBuffer msg) {
        return (isModeSupported(modeName)
                && validateSources(modeName, args, msg)
                && validateParameters(modeName, args, msg));
    }

    /**
     * Returns <code>true</code> if the operation should be computed immediately for all supported modes of this
     * operation during the call to <code>JAI.create()</code>; that is, the operation is placed in immediate mode. If
     * <code>true</code>, and the computation fails, <code>null</code> will be returned from <code>JAI.create()</code>.
     * If <code>false</code>, <code>JAI.create()</code> will return an instance of the appropriate destination class
     * that may be asked to compute itself at a later time; this computation may fail at that time.
     *
     * <p>Operations that rely on an external resource, such as a source file, or that produce externally-visible side
     * effects, such as writing to an output file, should return <code>true</code> from this method. Operations that
     * rely only on their sources and parameters usually wish to return <code>false</code> in order to defer rendering
     * as long as possible.
     *
     * <p>The default implementation in this class returns <code>false</code> so that deferred execution is invoked.
     * Operations that wish to be placed in the immediate mode must override this implementation.
     */
    public boolean isImmediate() {
        return false;
    }

    /**
     * Calculates the region over which two distinct renderings of an operation may be expected to differ.
     *
     * <p>The class of the returned object will vary as a function of the mode of the operation. For rendered and
     * renderable two- dimensional images this will be an instance of a class which implements <code>java.awt.Shape
     * </code>.
     *
     * @param modeName The name of the mode.
     * @param oldParamBlock The previous sources and parameters.
     * @param oldHints The previous hints.
     * @param newParamBlock The current sources and parameters.
     * @param newHints The current hints.
     * @param node The affected node in the processing chain.
     * @return <code>null</code> to indicate that there is no common region of validity.
     * @throws IllegalArgumentException if <code>modeName</code> is <code>null</code> or if the operation requires
     *     either sources or parameters and either <code>oldParamBlock</code> or <code>newParamBlock</code> is <code>
     *     null</code>.
     * @throws IllegalArgumentException if <code>oldParamBlock</code> or <code>newParamBlock</code> do not contain
     *     sufficient sources or parameters for the operation in question.
     * @since JAI 1.1
     */
    public Object getInvalidRegion(
            String modeName,
            ParameterBlock oldParamBlock,
            RenderingHints oldHints,
            ParameterBlock newParamBlock,
            RenderingHints newHints,
            OperationNode node) {
        if (modeName == null) throw new IllegalArgumentException(JaiI18N.getString("Generic0"));

        return null;
    }

    /**
     * Get the default source class for the supported mode.
     *
     * <p>"rendered" - java.awt.image.RenderedImage.class "renderable" - java.awt.image.renderable.RenderableImage.class
     * "collection" - java.util.Collection.class "renderableCollection" - java.util.Collection.class
     *
     * @since JAI 1.1
     */
    protected static Class getDefaultSourceClass(String modeName) {

        if ("rendered".equalsIgnoreCase(modeName)) return java.awt.image.RenderedImage.class;

        if ("renderable".equalsIgnoreCase(modeName)) return java.awt.image.renderable.RenderableImage.class;

        if ("collection".equalsIgnoreCase(modeName)) return java.util.Collection.class;

        if ("renderableCollection".equalsIgnoreCase(modeName)) return java.util.Collection.class;

        return null;
    }

    /**
     * Create a list of per mode source classes for each supported mode which can then be passed on to the constructor.
     * Uses <code>getDefaultSourceClass(modeName)</code> to construct this list.
     *
     * @since JAI 1.1
     */
    protected static Class[][] makeDefaultSourceClassList(String[] supportedModes, int numSources) {

        if ((supportedModes == null) || (supportedModes.length == 0)) return null;

        int count = supportedModes.length;

        Class classes[][] = new Class[count][numSources];

        for (int i = 0; i < count; i++) {

            Class sourceClass = getDefaultSourceClass(supportedModes[i]);

            for (int j = 0; j < numSources; j++) classes[i][j] = sourceClass;
        }

        return classes;
    }
    /** ******************** DEPRECATED Constructors ******************* */

    /** Create a list of supported modes using the deprecated is*Supported methods. */
    private String[] makeSupportedModeList() {

        int count = 0;

        if (isRenderedSupported()) count++;
        if (isRenderableSupported()) count++;

        String modes[] = new String[count];

        count = 0;

        if (isRenderedSupported()) modes[count++] = "rendered";
        if (isRenderableSupported()) modes[count++] = "renderable";

        return modes;
    }

    /** Create a list of per mode source classes for use by deprecated constructors. */
    private Class[][] makeSourceClassList(Class[] sourceClasses, Class[] renderableSourceClasses) {
        int count = 0;

        if (isRenderedSupported()) count++;
        if (isRenderableSupported()) count++;

        Class classes[][] = new Class[count][];

        count = 0;

        if (isRenderedSupported()) classes[count++] = sourceClasses;
        if (isRenderableSupported()) classes[count++] = renderableSourceClasses;

        return classes;
    }

    /** Create a list of valid parameter values, one for each param. */
    private Object[] makeValidParamValueList(Class[] paramClasses) {

        if (paramClasses == null) return null;

        int numParams = paramClasses.length;

        Object validValues[] = null;

        for (int i = 0; i < numParams; i++) {
            Number min = getParamMinValue(i);
            Number max = getParamMaxValue(i);

            if ((min == null) && (max == null)) continue;

            if (validValues == null) validValues = new Object[numParams];

            validValues[i] = new Range(min.getClass(), (Comparable) min, (Comparable) max);
        }

        return validValues;
    }

    /**
     * Constructor.
     *
     * @param resources The resource tags and their corresponding data.
     * @param sourceClasses The source types required by this operation in the rendered mode. It may be <code>null
     *     </code> if this operation does not support the rendered mode, or if it has no sources.
     * @param renderableSourceClasses The source types required by this operation in the renderable mode. It may be
     *     <code>null</code> if this operation does not support the renderable mode, or if it has no sources.
     * @param paramClasses The parameter types required by this operation. It may be <code>null</code> if this operation
     *     has no parameters.
     * @param paramNames The localized parameter names. It may be <code>null</code> if this operation has no parameters.
     * @param paramDefaults The parameter default values. It may be <code>null</code> if this operation has no
     *     parameters, or none of the parameters has a default value.
     * @throws IllegalArgumentException if <code>resources</code> is <code>null</code>.
     * @throws IllegalArgumentException if this operation supports the rendered mode, and it has sources, and <code>
     *     sourceClasses</code> is <code>null</code>.
     * @throws IllegalArgumentException if this operation supports the renderable mode, and it has sources, and <code>
     *     renderableSourceClasses</code> is <code>null</code>.
     * @throws IllegalArgumentException if <code>sourceClasses</code> and <code>renderableSourceClasses</code> (if both
     *     are not <code>null</code>) do not have the same number of elements.
     * @throws IllegalArgumentException if this operation has parameters and <code>paramClasses</code> or <code>
     *     paramNames</code> is <code>null</code>.
     * @throws IllegalArgumentException if this operation has parameters and <code>paramClasses</code>, <code>paramNames
     *     </code>, and <code>paramDefaults</code> (if all are not <code>null</code>) do not all have the same number of
     *     elements.
     * @deprecated as of JAI 1.1 in favor of constructors where supported modes are explicitly specified. Uses <code>
     *     isRenderedSupported()</code> and <code>
     * isRenderableSupported()</code> to figure out the supported modes.
     * @see OperationDescriptorImpl#OperationDescriptorImpl(String[][], String[], String[], Class [][], String[], Class
     *     [], Object[], Object[])
     */
    public OperationDescriptorImpl(
            String[][] resources,
            Class[] sourceClasses,
            Class[] renderableSourceClasses,
            Class[] paramClasses,
            String[] paramNames,
            Object[] paramDefaults) {
        this.deprecated = true;

        String[] supportedModes = makeSupportedModeList();
        Class[][] sourceClassList = makeSourceClassList(sourceClasses, renderableSourceClasses);

        String[] sourceNames = checkSources(resources, supportedModes, null, sourceClassList);

        Object[] validParamValues = makeValidParamValueList(paramClasses);

        this.resources = resources;
        this.supportedModes = supportedModes;
        this.sourceNames = sourceNames;
        this.sourceClasses = sourceClassList;
        this.paramNames = paramNames;

        this.modeIndices = new CaselessStringArrayTable(supportedModes);
        this.sourceIndices = new CaselessStringArrayTable(sourceNames);

        // Create one ParameterListDescriptor and use the same for each mode.

        ParameterListDescriptor pld =
                new ParameterListDescriptorImpl(this, paramNames, paramClasses, paramDefaults, validParamValues);

        paramListDescriptors = new ParameterListDescriptor[supportedModes.length];

        for (int i = 0; i < supportedModes.length; i++) {
            paramListDescriptors[i] = pld;
        }
    }

    /**
     * Constructor for operations that support either the rendered or the renderable or both modes. The class type for
     * all the source(s) of the rendered mode (if supported) is set to <code>java.awt.image.RenderedImage.class</code>.
     * The class type for all the source(s) of the renderable mode (if supported) is set to <code>
     * java.awt.image.renderable.RenderableImage</code>.
     *
     * @param resources The resource tags and their corresponding data.
     * @param numSources The number of sources required by this operation. It should not be negative. A negative value
     *     indicates this operation has no sources.
     * @param paramClasses The parameter types required by this operation. It may be <code>null</code> if this operation
     *     has no parameters.
     * @param paramNames The localized parameter names. It may be <code>null</code> if this operation has no parameters.
     * @param paramDefaults The parameter default values. It may be <code>null</code> if this operation has no
     *     parameters, or none of the parameters has a default value.
     * @throws IllegalArgumentException if <code>resources</code> is <code>null</code>.
     * @throws IllegalArgumentException if this operation has parameters and <code>paramClasses</code> or <code>
     *     paramNames</code> is <code>null</code>.
     * @throws IllegalArgumentException if this operation has parameters and <code>paramClasses</code>, <code>paramNames
     *     </code>, and <code>paramDefaults</code> (if not <code>null</code>) do not all have the same number of
     *     elements.
     * @deprecated as of JAI 1.1 in favor of constructors where supported modes are explicitly specified. Uses <code>
     *     isRenderedSupported()</code> and <code>
     * isRenderableSupported()</code> to figure out the supported modes.
     * @see OperationDescriptorImpl#OperationDescriptorImpl(String[][], String[], int, String[], Class [], Object[],
     *     Object[])
     */
    public OperationDescriptorImpl(
            String[][] resources, int numSources, Class[] paramClasses, String[] paramNames, Object[] paramDefaults) {
        this.deprecated = true;

        String[] supportedModes = makeSupportedModeList();
        Class[][] sourceClassList = makeDefaultSourceClassList(supportedModes, numSources);

        String[] sourceNames = checkSources(resources, supportedModes, null, sourceClassList);

        Object[] validParamValues = makeValidParamValueList(paramClasses);

        this.resources = resources;
        this.supportedModes = supportedModes;
        this.sourceNames = sourceNames;
        this.sourceClasses = sourceClassList;
        this.paramNames = paramNames;

        this.modeIndices = new CaselessStringArrayTable(supportedModes);
        this.sourceIndices = new CaselessStringArrayTable(sourceNames);

        // Create one ParameterListDescriptor and use the same for each mode.

        ParameterListDescriptor pld =
                new ParameterListDescriptorImpl(this, paramNames, paramClasses, paramDefaults, validParamValues);

        paramListDescriptors = new ParameterListDescriptor[supportedModes.length];

        for (int i = 0; i < supportedModes.length; i++) {
            paramListDescriptors[i] = pld;
        }
    }

    /**
     * Constructor for operations that supports only the rendered mode and requires no parameters.
     *
     * @param resources The resource tags and their corresponding data.
     * @param sourceClasses The source types required by this operation in the rendered mode. It may be <code>null
     *     </code> if this operation has no sources.
     * @throws IllegalArgumentException if <code>resources</code> is <code>null</code>.
     * @deprecated as of JAI 1.1 in favor of constructors where the mode specfic information is explicitly specified.
     * @see OperationDescriptorImpl#OperationDescriptorImpl(String[][], String[], String[], Class [][], String[], Class
     *     [], Object[], Object[])
     */
    public OperationDescriptorImpl(String[][] resources, Class[] sourceClasses) {
        this(resources, sourceClasses, null, null, null, null);
    }

    /**
     * Constructor for operations that supports either the rendered or the renderable or both modes and requires no
     * parameters.
     *
     * @param resources The resource tags and their corresponding data.
     * @param sourceClasses The source types required by this operation in the rendered mode. It may be <code>null
     *     </code> if this operation does not support the rendered mode, or if it has no sources.
     * @param renderableSourceClasses The source types required by this operation in the renderable mode. It may be
     *     <code>null</code> if this operation does not support the renderable mode, or if it has no sources.
     * @throws IllegalArgumentException if <code>resources</code> is <code>null</code>.
     * @throws IllegalArgumentException if this operation supports the rendered mode, and it has sources, and <code>
     *     sourceClasses</code> is <code>null</code>.
     * @throws IllegalArgumentException if this operation supports the renderable mode, and it has sources, and <code>
     *     renderableSourceClasses</code> is <code>null</code>.
     * @throws IllegalArgumentException if <code>sourceClasses</code> and <code>renderableSourceClasses</code> (if both
     *     are not <code>null</code>) do not have the same number of elements.
     * @deprecated as of JAI 1.1 in favor of constructors where the mode specfic information is explicitly specified.
     * @see OperationDescriptorImpl#OperationDescriptorImpl(String[][], String[], String[], Class [][], String[], Class
     *     [], Object[], Object[])
     */
    public OperationDescriptorImpl(String[][] resources, Class[] sourceClasses, Class[] renderableSourceClasses) {
        this(resources, sourceClasses, renderableSourceClasses, null, null, null);
    }

    /**
     * Constructor for operations that supports either the rendered or the renderable or both modes and requires no
     * sources.
     *
     * @throws IllegalArgumentException if <code>resources</code> is <code>null</code>.
     * @throws IllegalArgumentException if this operation has parameters and <code>paramClasses</code> or <code>
     *     paramNames</code> is <code>null</code>.
     * @throws IllegalArgumentException if this operation has parameters and <code>paramClasses</code>, <code>paramNames
     *     </code>, and <code>paramDefaults</code> (if not <code>null</code>) do not all have the same number of
     *     elements.
     * @deprecated as of JAI 1.1 in favor of constructors where the mode specfic information is explicitly specified.
     * @see OperationDescriptorImpl#OperationDescriptorImpl(String[][], String[], int, String[], Class [], Object[],
     *     Object[])
     */
    public OperationDescriptorImpl(
            String[][] resources, Class[] paramClasses, String[] paramNames, Object[] paramDefaults) {
        this(resources, null, null, paramClasses, paramNames, paramDefaults);
    }

    /**
     * Constructor for operations that support the rendered mode and possibly the renderable mode and require no
     * parameters. The class type for all the source(s) of the rendered mode is set to <code>
     * java.awt.image.RenderedImage.class</code>. The class type for all the source(s) of the renderable mode (if
     * supported) is set to <code>java.awt.image.renderable.RenderableImage</code>.
     *
     * @param resources The resource tags and their corresponding data.
     * @param numSources The number of sources required by this operation. It should not be negative. A negative value
     *     indicates this operation has no sources.
     * @throws IllegalArgumentException if <code>resources</code> is <code>null</code>.
     * @deprecated as of JAI 1.1 in favor of constructors where the mode specfic information is explicitly specified.
     * @see OperationDescriptorImpl#OperationDescriptorImpl(String[][], String[], int, String[], Class [], Object[],
     *     Object[])
     */
    public OperationDescriptorImpl(String[][] resources, int numSources) {
        this(resources, numSources, null, null, null);
    }

    /** ******************** DEPRECATED METHODS ************************ */

    /**
     * Returns an array of <code>PropertyGenerator</code>s implementing the property inheritance for this operation. The
     * default implementation returns <code>null</code>, indicating that source properties are simply copied. Subclasses
     * should override this method if they wish to produce inherited properties.
     *
     * @deprecated as of JAI 1.1 in favor of the equivalent method that specifies the mode name.
     * @see #getPropertyGenerators
     */
    public PropertyGenerator[] getPropertyGenerators() {
        return deprecated ? null : getPropertyGenerators("rendered");
    }

    /**
     * Returns <code>true</code> if this operation supports the rendered mode. The default implementation in this class
     * returns <code>true</code>.
     *
     * @deprecated as of JAI 1.1 in favor of <code>isModeSupported("rendered")</code>
     * @see #isModeSupported
     */
    public boolean isRenderedSupported() {
        return deprecated ? true : isModeSupported("rendered");
    }

    /**
     * Returns the source class types of this operation for the rendered mode. If this operation has no sources, this
     * method returns <code>null</code>.
     *
     * @deprecated as of JAI 1.1 in favor of <code>getSourceClasses("rendered")</code>
     * @throws IllegalArgumentException if the <code>rendered</code> mode is not supported.
     * @see #getSourceClasses
     */
    public Class[] getSourceClasses() {
        return getSourceClasses("rendered");
    }

    /**
     * Returns the destination class type of this operation for the rendered mode. The default implementation in this
     * class returns <code>java.awt.image.RenderedImage.class</code> if this operation supports the rendered mode, or
     * <code>null</code> otherwise.
     *
     * @deprecated as of JAI 1.1 in favor of <code>getDestClass("rendered")</code>
     * @see #getDestClass
     */
    public Class getDestClass() {
        if (deprecated) {
            return (isRenderedSupported() ? java.awt.image.RenderedImage.class : null);
        } else {
            return getDestClass("rendered");
        }
    }

    /**
     * Returns <code>true</code> if this operation supports the rendered mode, and is capable of handling the input
     * arguments for the rendered mode. The default implementation validates both the source(s) and the parameter(s).
     *
     * <p>Note that <code>DeferredData</code> parameters will not be recognized as valid unless the parameter is defined
     * to have class <code>DeferredData.class</code>.
     *
     * <p>Additional validations should be added by each individual operation based on its specification.
     *
     * @throws IllegalArgumentException if <code>args</code> is <code>null</code>.
     * @throws IllegalArgumentException if <code>msg</code> is <code>null</code> and the validation fails.
     * @deprecated as of JAI 1.1 in favor of <code>validateArguments("rendered", ...)</code>
     * @see #validateArguments
     */
    public boolean validateArguments(ParameterBlock args, StringBuffer msg) {
        if (deprecated) {
            return (validateSources(args, msg) && validateParameters(args, msg));
        } else {
            return validateArguments("rendered", args, msg);
        }
    }

    /** ******************* Renderable Mode Methods (deprecated) ******* */

    /**
     * Returns <code>true</code> if this operation supports the renderable mode. The default implementation in this
     * class returns <code>false</code>. Operations that support the renderable mode must override this implementation.
     *
     * @deprecated as of JAI 1.1 in favor of <code>isModeSupported("renderable")</code>
     * @see #isModeSupported
     */
    public boolean isRenderableSupported() {
        return deprecated ? false : isModeSupported("renderable");
    }

    /**
     * Returns the source class types of this operation for the renderable mode. If this operation has no sources this
     * method returns <code>null</code>.
     *
     * @deprecated as of JAI 1.1 in favor of <code>getSourceClasses("renderable")</code>
     * @throws IllegalArgumentException if the <code>renderable</code> mode is not supported.
     * @see #getSourceClasses
     */
    public Class[] getRenderableSourceClasses() {
        return getSourceClasses("renderable");
    }

    /**
     * Returns the destination class type of this operation for the renderable mode. The default implementation in this
     * class returns <code>java.awt.image.renderable.RenderableImage.class</code> if this operation supports the
     * renderable mode, or <code>null</code> otherwise.
     *
     * @deprecated as of JAI 1.1 in favor of <code>getDestClass("renderable")</code>
     * @see #getDestClass
     */
    public Class getRenderableDestClass() {
        if (deprecated) {
            return (isRenderableSupported() ? java.awt.image.renderable.RenderableImage.class : null);
        } else {
            return getDestClass("renderable");
        }
    }

    /**
     * Returns <code>true</code> if this operation supports the renderable mode, and is capable of handling the input
     * arguments for the renderable mode. The default implementation validates both the source(s) and the parameter(s).
     *
     * <p>If this operation does not support the renderable mode, this method returns <code>false</code> regardless of
     * the input arguments.
     *
     * <p>Note that <code>DeferredData</code> parameters will not be recognized as valid unless the parameter is defined
     * to have class <code>DeferredData.class</code>.
     *
     * <p>Additional validations should be added by each individual operation based on its specification.
     *
     * @throws IllegalArgumentException if <code>args</code> is <code>null</code>.
     * @throws IllegalArgumentException if <code>msg</code> is <code>null</code> and the validation fails.
     * @deprecated as of JAI 1.1 in favor of <code>validateArguments("renderable", ...)</code>
     * @see #validateArguments
     */
    public boolean validateRenderableArguments(ParameterBlock args, StringBuffer msg) {
        if (deprecated) {
            return (validateRenderableSources(args, msg) && validateParameters(args, msg));
        } else {
            return validateArguments("renderable", args, msg);
        }
    }

    /** ********************** Parameter Methods (deprecated) ********** */

    /**
     * The ParameterListDescriptor for the first supported mode. Used by deprecated methods where modeName is not
     * specified.
     */
    private ParameterListDescriptor getDefaultPLD() {
        return getParameterListDescriptor(getSupportedModes()[0]);
    }

    /**
     * Returns the number of parameters (not including sources) required by this operation.
     *
     * @deprecated as of JAI 1.1 in favor of <code>
     *      getParameterListDescriptor(modeName).getNumParameters()</code> This will for the time being return the above
     *     value for modeName = getSupportedModes()[0]
     * @see ParameterListDescriptor#getNumParameters
     */
    public int getNumParameters() {
        return getDefaultPLD().getNumParameters();
    }

    /**
     * Returns the parameter class types of this operation. If this operation has no parameters, this method returns
     * <code>null</code>.
     *
     * @deprecated as of JAI 1.1 in favor of <code>
     *      getParameterListDescriptor(modeName).getParamClasses()</code> This will for the time being return the above
     *     value for modeName = getSupportedModes()[0]
     * @see ParameterListDescriptor#getParamClasses
     */
    public Class[] getParamClasses() {
        return getDefaultPLD().getParamClasses();
    }

    /**
     * Returns the localized parameter names of this operation. If this operation has no parameters, this method returns
     * <code>null</code>.
     *
     * @deprecated as of JAI 1.1 in favor of <code>
     *      getParameterListDescriptor(modeName).getParamNames()</code> This will for the time being return the above
     *     value for modeName = getSupportedModes()[0]
     * @see ParameterListDescriptor#getParamNames
     */
    public String[] getParamNames() {
        return getDefaultPLD().getParamNames();
    }

    /**
     * Returns the default values of the parameters for this operation. If this operation has no parameters, this method
     * returns <code>null</code>. If a parameter does not have a default value, the constant <code>
     * OperationDescriptor.NO_PARAMETER_DEFAULT</code> will be returned. The <code>validateArguments()</code> and <code>
     * validateRenderableArguments</code> method will return <code>false</code> if an input parameter without a default
     * value is supplied as <code>null</code>, or if an unspecified tailing parameter does not have a default value.
     *
     * @deprecated as of JAI 1.1 in favor of <code>
     *      getParameterListDescriptor(modeName).getParamDefaults()</code> This will for the time being return the above
     *     value for modeName = getSupportedModes()[0]
     * @see ParameterListDescriptor#getParamDefaults
     */
    public Object[] getParamDefaults() {
        return getDefaultPLD().getParamDefaults();
    }

    /**
     * Returns the default value of specified parameter. The default value may be <code>null</code>. If a parameter has
     * no default value, this method returns <code>OperationDescriptor.NO_PARAMETER_DEFAULT</code>.
     *
     * @param index The index of the parameter whose default value is queried.
     * @throws IllegalArgumentException if this operation has no parameters.
     * @throws ArrayIndexOutOfBoundsException if there is no parameter corresponding to the specified <code>index</code>
     *     .
     * @deprecated as of JAI 1.1 in favor of <code>
     *      getParameterListDescriptor(modeName).getParamDefaultValue(...)</code> This will for the time being return
     *     the above value for modeName = getSupportedModes()[0]
     * @see ParameterListDescriptor#getParamDefaultValue
     */
    public Object getParamDefaultValue(int index) {
        return getDefaultPLD().getParamDefaultValue(paramNames[index]);
    }

    /**
     * Returns the minimum legal value of a specified numeric parameter for this operation. If the specified parameter
     * is non-numeric, this method returns <code>null</code>.
     *
     * <p>The return value should be of the class types corresponding to the parameter's primitive type, that is, <code>
     * Byte</code> for a <code>byte</code> parameter, <code>Integer</code> for an <code>int</code> parameter, and so
     * forth.
     *
     * <p>The default implementation returns the minimum value in the parameter data type's full range.
     *
     * @param index The index of the parameter to be queried.
     * @return A <code>Number</code> representing the minimum legal value, or <code>null</code> if the specified
     *     parameter is not numeric.
     * @throws IllegalArgumentException if this operation has no parameters.
     * @throws ArrayIndexOutOfBoundsException if there is no parameter corresponding to the specified <code>index</code>
     *     .
     * @deprecated as of JAI 1.1 in favor of <code>
     *      getParameterListDescriptor(modeName).getParamValueRange()</code> This will for the time being use <code>
     *     getSupportedModes()[0]
     *      </code> for modeName.
     *     <p>If the parameter is not a sub-class of the <code>Number</code> class then this method returns null.
     *     <p>Else if the above getParamValueRange() returns a non-null <code>Range</code> then it returns the <code>
     *     getMinValue()</code> of that <code>Range</code>.
     *     <p>Else for <code>Float</code> and <code>Double</code> parameters it returns the corresponding <code>
     *     -MAX_VALUE</code> and <code>MIN_VALUE</code> for other <code>Number</code> classes.
     * @see ParameterListDescriptor#getParamValueRange
     * @see ParameterListDescriptor#getEnumeratedParameterValues
     * @see ParameterListDescriptor#isParameterValueValid
     */
    public Number getParamMinValue(int index) {
        return null;
    }

    /**
     * Returns the maximum legal value of a specified numeric parameter for this operation. If the specified parameter
     * is non-numeric, this method returns <code>null</code>.
     *
     * <p>The return value should be of the class type corresponding to the parameter's primitive type, that is, <code>
     * Byte</code> for a <code>byte</code> parameter, <code>Integer</code> for an <code>int</code> parameter, and so
     * forth.
     *
     * <p>The default implementation returns the maximum value in the parameter data type's full range.
     *
     * @param index The index of the parameter to be queried.
     * @return A <code>Number</code> representing the maximum legal value, or <code>null</code> if the specified
     *     parameter is not numeric.
     * @throws IllegalArgumentException if this operation has no parameters.
     * @throws ArrayIndexOutOfBoundsException if there is no parameter corresponding to the specified <code>index</code>
     *     .
     * @deprecated as of JAI 1.1 in favor of <code>
     *      getParameterListDescriptor(modeName).getParamValueRange()</code> This will for the time being use <code>
     *     getSupportedModes()[0]
     *      </code> for modeName.
     *     <p>If the parameter is not a sub-class of the <code>Number</code> class then this method returns null.
     *     <p>Else if the above getParamValueRange() returns a non-null <code>Range</code> then it returns the <code>
     *     getMaxValue()</code> of that <code>Range</code>.
     *     <p>Else returns the <code>MAX_VALUE</code> of the corresponding <code>Number</code> class.
     * @see ParameterListDescriptor#getParamValueRange
     * @see ParameterListDescriptor#getEnumeratedParameterValues
     * @see ParameterListDescriptor#isParameterValueValid
     */
    public Number getParamMaxValue(int index) {
        return null;
    }

    /**
     * Returns <code>true</code> if this operation supports the rendered mode, and is capable of handling the given
     * input source(s) for the rendered mode. The default implementation validates the number of sources, the class type
     * of each source, and null sources. Subclasses should override this implementation if their requirement on the
     * sources are different from the default.
     *
     * @throws IllegalArgumentException if <code>args</code> is <code>null</code>.
     * @throws IllegalArgumentException if <code>msg</code> is <code>null</code> and the validation fails.
     * @deprecated as of JAI 1.1 in favor of <code>
     * 	validateSources("rendered", ...)</code>
     * @see #validateSources
     */
    protected boolean validateSources(ParameterBlock args, StringBuffer msg) {
        if (deprecated) {
            return (isRenderedSupported() && validateSources(getSourceClasses(), args, msg));
        } else {
            return validateSources("rendered", args, msg);
        }
    }

    /**
     * Returns <code>true</code> if this operation supports the renderable mode, and is capable of handling the given
     * input source(s) for the renderable mode. The default implementation validates the number of sources, the class
     * type of each source, and null sources. Subclasses should override this implementation if their requirement on the
     * sources are different from the default.
     *
     * @throws IllegalArgumentException if <code>args</code> is <code>null</code>.
     * @throws IllegalArgumentException if <code>msg</code> is <code>null</code> and the validation fails.
     * @deprecated as of JAI 1.1 in favor of <code>
     * 	validateSources("renderable", ...)</code>
     * @see #validateSources
     */
    protected boolean validateRenderableSources(ParameterBlock args, StringBuffer msg) {
        if (deprecated) {
            return (isRenderableSupported() && validateSources(getRenderableSourceClasses(), args, msg));
        } else {
            return validateSources("renderable", args, msg);
        }
    }

    /**
     * Returns <code>true</code> if this operation is capable of handling the given input parameters. The default
     * implementation validates the number of parameters, the class type of each parameter, and null parameters. For
     * those non-null numeric parameters, it also checks to see if the parameter value is within the minimum and maximum
     * range. Subclasses should override this implementation if their requirements for the parameter objects are
     * different from the default.
     *
     * <p>JAI allows unspecified tailing parameters if these parameters have default values. This method automatically
     * sets these unspecified parameters to their default values. However, if a parameter, which has a default value, is
     * followed by one or more parameters that have no default values, this parameter must be specified in the <code>
     * ParameterBlock</code>; else this method returns <code>false</code>.
     *
     * <p>Note that <code>DeferredData</code> parameters will not be recognized as valid unless the parameter is defined
     * to have class <code>DeferredData.class</code>.
     *
     * @throws IllegalArgumentException if <code>args</code> is <code>null</code>.
     * @throws IllegalArgumentException if <code>msg</code> is <code>null</code> and the validation fails.
     * @deprecated as of JAI 1.1 in favor of <code>
     * 	validateParameters(getSupportedModes()[0], ...)</code>
     * @see #validateParameters
     */
    protected boolean validateParameters(ParameterBlock args, StringBuffer msg) {
        return validateParameters(getDefaultPLD(), args, msg);
    }

    /** Returns the minimum number of parameters must be supplied in the <code>ParameterBlock</code>. */
    private int getMinNumParameters(ParameterListDescriptor pld) {
        // The number of parameters this operation should have.
        int numParams = pld.getNumParameters();

        Object paramDefaults[] = pld.getParamDefaults();

        for (int i = numParams - 1; i >= 0; i--) {
            if (paramDefaults[i] == ParameterListDescriptor.NO_PARAMETER_DEFAULT) {
                break;
            } else {
                numParams--;
            }
        }

        return numParams;
    }

    private boolean validateSources(Class[] sources, ParameterBlock args, StringBuffer msg) {

        if ((args == null) || (msg == null)) throw new IllegalArgumentException(JaiI18N.getString("Generic0"));

        // The number of sources this operation requires.
        int numSources = getNumSources();

        // Check for the correct number of sources.
        if (args.getNumSources() < numSources) {
            msg.append(
                    JaiI18N.formatMsg("OperationDescriptorImpl6", new Object[] {getName(), new Integer(numSources)}));
            return false;
        }

        for (int i = 0; i < numSources; i++) {
            Object s = args.getSource(i);

            // Check for null source.
            if (s == null) {
                msg.append(JaiI18N.formatMsg("OperationDescriptorImpl7", new Object[] {getName()}));
                return false;
            }

            // Check for the correct class of each supplied source.
            Class c = sources[i];
            if (!c.isInstance(s)) {
                msg.append(JaiI18N.formatMsg("OperationDescriptorImpl8", new Object[] {
                    getName(),
                    new Integer(i),
                    new String(c.toString()),
                    new String(s.getClass().toString())
                }));
                return false;
            }
        }

        return true;
    }

    private boolean validateParameters(ParameterListDescriptor pld, ParameterBlock args, StringBuffer msg) {

        if ((args == null) || (msg == null)) throw new IllegalArgumentException(JaiI18N.getString("Generic0"));

        // The number of parameters this operation should have.
        int numParams = pld.getNumParameters();

        // The number of parameters supplied.
        int argNumParams = args.getNumParameters();

        Object paramDefaults[] = pld.getParamDefaults();

        // Check for the correct number of parameters.
        if (argNumParams < numParams) {
            // The minimum number of parameters this operation must have.
            if (argNumParams < getMinNumParameters(pld)) {
                msg.append(JaiI18N.formatMsg(
                        "OperationDescriptorImpl9", new Object[] {getName(), new Integer(numParams)}));

                return false;

            } else { // use default values
                for (int i = argNumParams; i < numParams; i++) {
                    args.add(paramDefaults[i]);
                }
            }
        }

        for (int i = 0; i < numParams; i++) {
            Object p = args.getObjectParameter(i);

            /* Check for null parameter. */
            if (p == null) {
                p = paramDefaults[i]; // get the default parameter value

                if (p == OperationDescriptor.NO_PARAMETER_DEFAULT) {
                    msg.append(
                            JaiI18N.formatMsg("OperationDescriptorImpl11", new Object[] {getName(), new Integer(i)}));
                    return false;

                } else {
                    args.set(p, i); // replace null parameter with default
                }
            }

            // Now check if the parameter value is valid
            try {
                if (!pld.isParameterValueValid(paramNames[i], p)) {
                    msg.append(JaiI18N.formatMsg(
                            "OperationDescriptorImpl10", new Object[] {getName(), pld.getParamNames()[i]}));
                    return false;
                }
            } catch (IllegalArgumentException e) {
                msg.append(getName() + " - " + e.getLocalizedMessage());
                return false;
            }
        }

        return true;
    }

    /** Make sure that <code>modeName</code> is not <code>null</code> and is one of the supported modes. */
    private void checkModeName(String modeName) {

        if (modeName == null) throw new IllegalArgumentException(JaiI18N.getString("OperationDescriptorImpl12"));

        if (modeIndices.contains(modeName) == false) {
            throw new IllegalArgumentException(
                    JaiI18N.formatMsg("OperationDescriptorImpl13", new Object[] {getName(), modeName}));
        }
    }
}
